package com.sz.biz.trade.bill.service.impl;

import com.github.tobato.fastdfs.domain.StorePath;
import com.github.tobato.fastdfs.exception.FdfsServerException;
import com.github.tobato.fastdfs.proto.ErrorCodeConstants;
import com.github.tobato.fastdfs.service.FastFileStorageClient;
import com.google.common.collect.Lists;
import com.sz.biz.common.AbstractVersionEntityService;
import com.sz.biz.common.approval.Approval;
import com.sz.biz.common.approval.ApprovalService;
import com.sz.biz.common.constants.ModuleNames;
import com.sz.biz.common.customer.entity.CusUser;
import com.sz.biz.common.customer.service.CustomerUserService;
import com.sz.biz.common.platform.entity.PlatformCompany;
import com.sz.biz.common.platform.service.PlatformCompanyService;
import com.sz.biz.common.project.service.ProjectService;
import com.sz.biz.common.sup.dto.SupplierDto;
import com.sz.biz.common.sup.entity.Supplier;
import com.sz.biz.common.sup.service.SupplierService;
import com.sz.biz.logistics.bill.dto.BillingDto;
import com.sz.biz.logistics.bill.dto.BillingListCustomerDto;
import com.sz.biz.logistics.bill.dto.BillingUrlDto;
import com.sz.biz.logistics.bill.entity.BillingAdjustmentTypeCode;
import com.sz.biz.logistics.bill.entity.BillingStatus;
import com.sz.biz.logistics.bill.entity.BillingTaskStatus;
import com.sz.biz.logistics.constants.ErrorCodes;
import com.sz.biz.logistics.constants.QuotationConstants;
import com.sz.biz.trade.bill.dto.*;
import com.sz.biz.trade.bill.entity.TrdBilling;
import com.sz.biz.trade.bill.entity.TrdBillingOrderRel;
import com.sz.biz.trade.bill.entity.TrdBillingTask;
import com.sz.biz.trade.bill.service.TrdBillingOrderRelService;
import com.sz.biz.trade.bill.service.TrdBillingService;
import com.sz.biz.trade.bill.service.TrdBillingTaskService;
import com.sz.biz.trade.ord.service.TrdOrderService;
import com.sz.common.base.constants.UserActions;
import com.sz.common.base.dao.ParamData;
import com.sz.common.base.dto.CommonModel;
import com.sz.common.base.dto.QResultDto;
import com.sz.common.base.dto.QueryParams;
import com.sz.common.base.exception.ErrorCode;
import com.sz.common.base.exception.Exceptions;
import com.sz.common.base.fastdfs.FastDFSClientWrapper;
import com.sz.common.base.utils.DateUtils;
import com.sz.common.base.utils.EntityUtils;
import com.sz.common.base.utils.StringUtils;
import com.sz.common.core.service.PrincipalLogUtils;
import com.sz.common.core.service.PrincipalUtils;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.apache.poi.xssf.streaming.SXSSFCell;
import org.apache.poi.xssf.streaming.SXSSFRow;
import org.apache.poi.xssf.streaming.SXSSFSheet;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import org.apache.tomcat.util.http.fileupload.ByteArrayOutputStream;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.io.ByteArrayInputStream;
import java.math.BigDecimal;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * Function: 账单服务<br>
 * Author: changwentao <br>
 * Date: 2017-07-18 14:10:00
 */
@Service
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public class TrdBillingServiceImpl extends AbstractVersionEntityService implements TrdBillingService {

    private static final String NUMBER_REGEX = "^(([1-9]{1}\\d*)|([0]{1}))(\\.(\\d){0,3})?$";
    private static final String SUP_ = "sup_";
    private static final int AMOUNT_LEN = 10;
    private static final int PERCENT_LEN = 5;
    @Autowired
    private TrdBillingOrderRelService trdBillingOrderRelService;
    @Autowired
    private ApprovalService approvalService;
    @Autowired
    private TrdOrderService trdOrderService;
    @Autowired
    protected TrdBillingTaskService trdBillingTaskService;
    @Autowired
    private FastFileStorageClient storageClient;
    @Autowired
    private CustomerUserService customerUserService;
    @Autowired
    private ProjectService projectService;
    @Autowired
    private PlatformCompanyService platformCompanyService;
    @Autowired
    private SupplierService supplierService;
    @Autowired
    private FastDFSClientWrapper fastDFSClientWrapper;

    /**
     * 批量删除账单
     *
     * @param billNos billNos
     */
    @Override
    public void batchdeleteByNos(List<String> billNos) {
        if (null == billNos || billNos.size() <= 0) {
            throw Exceptions.bizException(ErrorCodes.ERROR_ARGUMENT_ILLEGAL);
        }
        // 判断要删除的账单状态是否包含其它非草稿状态
        checkStatusPreDelete(billNos, BillingStatus.DRAFT, ErrorCodes.ERROR_FINANCIAL_DRAFT_CAN_DELETE);
        deleteBills(billNos, BillingStatus.DRAFT);
    }

    /**
     * 批量删除账单信息-客户平台
     * 只能删除已付款的账单
     *
     * @param billNos billNos
     */
    @Override
    public void batchdeleteByNosCustomer(List<String> billNos) {
        if (null == billNos || billNos.size() <= 0) {
            throw Exceptions.bizException(ErrorCodes.ERROR_ARGUMENT_ILLEGAL);
        }
        // 判断要删除的账单状态是否包含其它非草稿状态
        checkStatusPreDelete(billNos, BillingStatus.PAID, ErrorCodes.ERROR_FINANCIAL_PAID_CAN_DELETE);
        deleteBills(billNos, BillingStatus.PAID);
    }

    /**
     * 删除账单
     *
     * @param billNos billNos
     */
    private void deleteBills(List<String> billNos, BillingStatus status) {

        TrdBilling trdBilling = findByBillNo(billNos.get(0));
        // 删除账单下所有关联订单
        trdBillingOrderRelService.deleteByBillNos(billNos, trdBilling.getType());

        ParamData paramData = new ParamData();
        paramData.put("billNos", billNos);
        paramData.put("status", status);
        dao.delete(("TrdBillingMapper.batchdeleteByNos"), paramData);
    }


    /**
     * 只有草稿/已付款状态的可以删除
     *
     * @param billNos billNos
     */
    private void checkStatusPreDelete(List<String> billNos, BillingStatus statusDelete, ErrorCode errorCode) {
        ParamData paramData = new ParamData();
        paramData.put("billNos", billNos);
        List<TrdBilling> bills = dao.findForList(("TrdBillingMapper.findActivedByBillNos"), paramData, TrdBilling.class);
        if (null != bills && bills.size() > 0) {
            for (TrdBilling bill : bills) {
                if (!bill.getStatus().getCode().equals(statusDelete.getCode())) {
                    throw Exceptions.bizException(errorCode);
                }
            }
        }
    }

    /**
     * 根据任务编码删除账单
     *
     * @param taskCodes taskCodes
     */
    @Override
    public void batchDeleteByTaskCodes(List<String> taskCodes) {
        ParamData paramData = new ParamData();
        paramData.put("taskCodes", taskCodes);
        dao.delete(("TrdBillingMapper.batchDeleteByTaskCodes"), paramData);
    }

    /**
     * 客户平台账单流转，已出帐，已付款，逾期未结
     *
     * @param billNo billNo
     * @return List
     */
    @Override
    public List<Map<String, String>> findFinancialApprovals(String billNo) {
        List<String> billingStatus = new ArrayList<>();
        billingStatus.add(BillingStatus.CHARGEOFF.getCode());
        billingStatus.add(BillingStatus.PAID.getCode());
        billingStatus.add(BillingStatus.OVERDUE.getCode());
        List<Map<String, String>> approvals = new ArrayList<>();
        List<Approval> approvalList = approvalService.getApprovalsForBill(ApprovalService.ENTITY_TYPE_FINANCIAL, billNo, billingStatus);
        if (null != approvalList && approvalList.size() > 0) {
            for (Approval approval : approvalList) {
                Map<String, String> map = new HashMap<>();
                map.put("status", BillingStatus.valueOfEnum(approval.getToStatus() + "").getName());
                map.put("billNo", approval.getOwnerId());
                map.put("time", DateUtils.formatDetailDate(approval.getApprovalTime()));
                approvals.add(map);
            }
        }
        return approvals;
    }

    /**
     * 已付款
     *
     * @param billNos billNos
     */
    @Override
    public void paid(List<String> billNos) {
        if (CollectionUtils.isEmpty(billNos)) {
            return;
        }
        // 修改账单状态
        updateBillingStatus(billNos, BillingStatus.CHARGEOFF, BillingStatus.PAID, "", "账单已付款", null);
    }

    /**
     * 导出账单
     *
     * @param billNos billNos
     */
    @Override
    public List<BillingUrlDto> export(List<String> billNos) {
        //只有已出帐账单可以导出
        checkExportBills(billNos);
        ParamData pd = new ParamData();
        pd.put("billNos", billNos);
        List<BillingUrlDto> list = dao.findForList(("TrdBillingMapper.findURLByBillNos"), pd, BillingUrlDto.class);
        //容错
        if (!CollectionUtils.isEmpty(list)) {
            list.forEach(billing -> {
                boolean isRefetch = false;
                if (!StringUtils.isEmpty(billing.getUrl())) {
                    try {
                        fastDFSClientWrapper.getFileMetaInfo(billing.getUrl());
                    } catch (FdfsServerException exception) {
                        if (exception.getErrorCode() == ErrorCodeConstants.ERR_NO_ENOENT) {
                            exportBills(Collections.singletonList(billing.getBillNo()));
                            isRefetch = true;
                        }
                    }
                } else {
                    exportBills(Collections.singletonList(billing.getBillNo()));
                    isRefetch = true;
                }
                if (isRefetch) {
                    ParamData pdRe = new ParamData();
                    pdRe.put("billNos", Collections.singletonList(billing.getBillNo()));
                    List<BillingUrlDto> listRe = dao.findForList("TrdBillingMapper.findURLByBillNos", pdRe, BillingUrlDto.class);
                    if (!CollectionUtils.isEmpty(listRe)) {
                        billing.setUrl(listRe.get(0).getUrl());
                    }
                }
            });
        }
        return list;
    }

    /**
     * 导出账单-客户平台
     *
     * @param billNos billNos
     */
    @Override
    public List<BillingUrlDto> exportCustomer(List<String> billNos) {
        //只有已出帐账单可以导出
        checkExportBills(billNos);
        //是否主帐号判断
        Integer userId = PrincipalUtils.getAccountId();
        //帐号信息
        CusUser primaryAccount = isPrimaryAccount(userId);
        if ((null != primaryAccount && primaryAccount.getIsSystem()) || PrincipalUtils.getAccountName().startsWith(SUP_)) {
            ParamData pd = new ParamData();
            pd.put("billNos", billNos);
            pd.put("customerId", primaryAccount.getRefCustomerId());
            List<BillingUrlDto> list = dao.findForList(("TrdBillingMapper.findURLByBillNos"), pd, BillingUrlDto.class);
            //容错
            if (!CollectionUtils.isEmpty(list)) {
                list.forEach(billing -> {
                    boolean isRefetch = false;
                    if (!StringUtils.isEmpty(billing.getUrl())) {
                        try {
                            fastDFSClientWrapper.getFileMetaInfo(billing.getUrl());
                        } catch (FdfsServerException exception) {
                            if (exception.getErrorCode() == ErrorCodeConstants.ERR_NO_ENOENT) {
                                exportBills(Collections.singletonList(billing.getBillNo()));
                                isRefetch = true;
                            }
                        }
                    } else {
                        exportBills(Collections.singletonList(billing.getBillNo()));
                        isRefetch = true;
                    }
                    if (isRefetch) {
                        ParamData pdRe = new ParamData();
                        pdRe.put("billNos", Collections.singletonList(billing.getBillNo()));
                        pd.put("customerId", primaryAccount.getRefCustomerId());
                        List<BillingUrlDto> listRe = dao.findForList("TrdBillingMapper.findURLByBillNos", pdRe, BillingUrlDto.class);
                        if (!CollectionUtils.isEmpty(listRe)) {
                            billing.setUrl(listRe.get(0).getUrl());
                        }
                    }
                });
            }
            return list;
        } else {
            //只有主帐号可以导出账单
            throw Exceptions.bizException(ErrorCodes.ERROR_FINANCIAL_PRIMARY_ACCOUNT_CAN_EXPORT);
        }
    }

    private void exportBills(List<String> billNos) {
        List<TrdBillingAndOrderListDto> list = new ArrayList<TrdBillingAndOrderListDto>();
        if (billNos != null && billNos.size() > 0) {
            int type = 0;
            for (String billNo : billNos) {
                TrdBillingAndOrderListDto billingAndOrderListDto = new TrdBillingAndOrderListDto();
                TrdBillingListDto billingListDto = findBillingInfo(billNo);
                EntityUtils.copyPropertiesIgnoreNull(billingListDto, billingAndOrderListDto);
                // 账单下所有订单
                List<TrdBillingOrderListDto> billingOrderListDtoList = trdBillingOrderRelService.findOrdersByBillNo(billingListDto.getBillNo());
                billingAndOrderListDto.setOrderList(billingOrderListDtoList);
                billingAndOrderListDto.setPlatformName(billingOrderListDtoList.get(0).getPlatformName());
                type = billingListDto.getType();
                if (type == 2) {
                    Supplier sup = supplierService.findSimpleById(billingAndOrderListDto.getBizCompanyId());
                    billingAndOrderListDto.setBizCompanyId(sup.getType());
                    billingAndOrderListDto.setBizCompanyCode(sup.getCode());
                    billingAndOrderListDto.setBizCompanyName(sup.getName());
                }
                list.add(billingAndOrderListDto);
            }
            exportXlsx(type, list);
        }
    }

    private void exportXlsx(int type, List<TrdBillingAndOrderListDto> list) {
        for (TrdBillingAndOrderListDto dto : list) {
            StorePath storePath;
            String filename;
            // 第一步，创建一个webbook，对应一个Excel文件
            SXSSFWorkbook wb = new SXSSFWorkbook(1000);
            String bill = dto.getBillNo();
            filename = dto.getBizCompanyName() + "(" + dto.getBizCompanyCode() + ")" + bill + "账单.xlsx";
            List<TrdBillingOrderListDto> orderList = dto.getOrderList();
            // 第二步，在webbook中添加一个sheet,对应Excel文件中的sheet
            List<List<TrdBillingOrderListDto>> lists = new ArrayList<List<TrdBillingOrderListDto>>();
            if (orderList.size() > 65500) {
                int mod = orderList.size() % 65500;
                int count = orderList.size() / 65500;
                int num;
                if (mod > 0) {
                    num = (int) Math.floor(count + 1);
                } else {
                    num = (int) Math.floor(count);
                }
                for (int i = 0; i < num; i++) {
                    List<TrdBillingOrderListDto> listOrder = new ArrayList<TrdBillingOrderListDto>();
                    if (i >= num) {
                        for (int j = 0; j < mod; j++) {
                            TrdBillingOrderListDto billingOrderListDto = orderList.get(i * 65500 + j);
                            listOrder.add(billingOrderListDto);
                        }

                    } else {
                        for (int j = 0; j < 65500; j++) {
                            TrdBillingOrderListDto billingOrderListDto = orderList.get(i * 65500 + j);
                            listOrder.add(billingOrderListDto);
                        }
                    }
                    lists.add(listOrder);
                }
                setCell(type, wb, dto, bill, lists);
            } else {
                lists.add(orderList);
                setCell(type, wb, dto, bill, lists);
            }
            try {
                // 第六步，将文件上传文件服务器
                ByteArrayOutputStream os = new ByteArrayOutputStream();
                wb.write(os);
                ByteArrayInputStream swapStream = new ByteArrayInputStream(os.toByteArray());
                storePath = storageClient.uploadFile(swapStream, os.toByteArray().length,
                        FilenameUtils.getExtension("export.xlsx"), null);
                // 第七步，更新数据库
                ParamData pd = new ParamData();
                String url = storePath.getFullPath() + "?filename=" + filename;
                pd.put("url", url);
                pd.put("billNo", dto.getBillNo());
                dao.update(("TrdBillingMapper.updateURL"), pd);
                os.close();
                wb.close();
            } catch (Exception e) {
                System.out.println(e.toString());
                e.printStackTrace();
            }
        }

    }

    /**
     * 只有已出帐账单可以导出
     *
     * @param billNos billNos
     */
    private void checkExportBills(List<String> billNos) {
        List<String> billNoUrls = new ArrayList<String>();
        ParamData paramData = new ParamData();
        paramData.put("billNos", billNos);
        List<TrdBilling> bills = dao.findForList(("TrdBillingMapper.findActivedByBillNos"), paramData, TrdBilling.class);
        if (null != bills && bills.size() > 0) {
            for (TrdBilling bill : bills) {
                if (!bill.getStatus().getCode().equals(BillingStatus.CHARGEOFF.getCode())
                        && !bill.getStatus().getCode().equals(BillingStatus.PAID.getCode())
                        && !bill.getStatus().getCode().equals(BillingStatus.OVERDUE.getCode())) {
                    throw Exceptions.bizException(ErrorCodes.ERROR_FINANCIAL_CHARGEOFF_CAN_EXPORT);
                }
                if (bill.getUrl() == null || "".equals(bill.getUrl())) {
                    billNoUrls.add(bill.getBillNo());
                }
            }
        }
        if (billNoUrls != null && billNoUrls.size() > 0) {
            exportBills(billNoUrls);
        }

    }

    /**
     * 填充cell值
     *
     * @param wb    wb
     * @param dto   dto
     * @param bill  bill
     * @param lists lists
     */
    private void setCell(int type, SXSSFWorkbook wb, TrdBillingAndOrderListDto dto, String bill, List<List<TrdBillingOrderListDto>> lists) {
        int sheetPage = 0;
        TrdBilling trdbill = findByBillNo(bill);
        for (List<TrdBillingOrderListDto> orderList : lists) {
            SXSSFSheet sheet = wb.createSheet(bill + "-" + String.valueOf(sheetPage));
            sheet.trackAllColumnsForAutoSizing();
            sheetPage = sheetPage + 1;
            // 第三步，创建单元格，并设置值表头 设置表头居中
            CellStyle style = wb.createCellStyle();
            style.setWrapText(true);
            // 第四步，在sheet中添加表头第0行,注意老版本poi对Excel的行数列数有限制short
            SXSSFRow row0;
            style.setAlignment(HorizontalAlignment.CENTER); // 创建一个居中格式
            setCellHead(sheet, style, "账单号", dto.getBillNo(), 0, null, 0);
            setCellHead(sheet, style, trdbill.getType() == 1 ? "付款方" : "收款方",
                    dto.getBizCompanyName() + "(" + dto.getBizCompanyCode() + ")", 1, null, 0);
            setCellHead(sheet, style, trdbill.getType() == 1 ? "收款方" : "付款方", dto.getPlatformName(), 2, null, 0);

            setCellHead(sheet, style, "账单周期", (DateUtils.formatDetailDate(dto.getStartEffectTime())) + " ~ "
                    + (DateUtils.formatDetailDate(dto.getEndEffectTime())), 3, null, 0);
            setCellHead(sheet, style, "生成时间", (DateUtils.formatDetailDate(dto.getReleaseTime())), 4, null, 0);
            setCellHead(sheet, style, "状态", dto.getStatus().getName(), 5, null, 0);
            BigDecimal total = dto.getTotal();
            if (null != total) {
                total = total.setScale(3, BigDecimal.ROUND_HALF_UP);
            }
            BigDecimal actualTotal = dto.getActualTotal();
            if (null != actualTotal) {
                actualTotal = actualTotal.setScale(2, BigDecimal.ROUND_HALF_UP);
            } else {
                actualTotal = dto.getTotal().setScale(2, BigDecimal.ROUND_HALF_UP);
            }

            setCellHead(sheet, style, "币种", dto.getCurrencyType(), 6, null, 0);
            setCellHead(sheet, style, "应收总计", total.toString(), 7, null, 0);
            row0 = setCellHead(sheet, style, "价格调整", dto.getAdjustment(), 8, null, 0);
            setCellHead(sheet, style, "调整说明", dto.getAdjustmentRemark(), 8, row0, 2);

            setCellHead(sheet, style, "实收总计", actualTotal.toString(), 9, null, 0);

            //空两行
            sheet.createRow(10);
            sheet.createRow(11);

            SXSSFRow row11 = sheet.createRow(12);
            SXSSFCell cell11 = row11.createCell(0);
            cell11.setCellValue("订单号");
            if (type == 1) {
                cell11 = row11.createCell(1);
                cell11.setCellValue("账号名");
                cell11 = row11.createCell(2);
                cell11.setCellValue("商品名称");
                cell11 = row11.createCell(3);
                cell11.setCellValue("计费时间");
                cell11 = row11.createCell(4);
                cell11.setCellValue("合计(" + dto.getCurrencyType() + ")");
            } else {
                cell11 = row11.createCell(1);
                cell11.setCellValue("商品名称");
                cell11 = row11.createCell(2);
                cell11.setCellValue("计费时间");
                cell11 = row11.createCell(3);
                cell11.setCellValue("合计(" + dto.getCurrencyType() + ")");
            }
            // 第五步，写入实体数据 实际应用中这些数据从数据库得到，
            for (int i = 0; i < orderList.size(); i++) {
                row11 = sheet.createRow(i + 13);
                TrdBillingOrderListDto billingOrderListDto = orderList.get(i);
                // 第四步，创建单元格，并设置值
                row11.createCell(0).setCellValue(billingOrderListDto.getOrderNo());
                BigDecimal amountDue = billingOrderListDto.getAmountDue();
                if(null == amountDue){
                    amountDue = BigDecimal.ZERO;
                }
                if (type == 1) {
                    row11.createCell(1).setCellValue(billingOrderListDto.getCompanyName() + "(" + billingOrderListDto.getCompanyUserName() + ")");
                    String goodsNames = billingOrderListDto.getGoodsNames().substring(1, billingOrderListDto.getGoodsNames().length() - 1);
                    row11.createCell(2).setCellValue(goodsNames);
                    row11.createCell(3).setCellValue(DateUtils.formatDetailDate(billingOrderListDto.getBillTime()));
                    row11.createCell(4).setCellValue("" + amountDue.setScale(3, BigDecimal.ROUND_HALF_UP));
                } else {
                    String goodsNames = billingOrderListDto.getGoodsNames().substring(1, billingOrderListDto.getGoodsNames().length() - 1);
                    row11.createCell(1).setCellValue(goodsNames);
                    row11.createCell(2).setCellValue(DateUtils.formatDetailDate(billingOrderListDto.getBillTime()));
                    row11.createCell(3).setCellValue("" + amountDue.setScale(3, BigDecimal.ROUND_HALF_UP));
                }
            }
            // 自动列宽
            sheet.autoSizeColumn(0); // 调整第一列宽度
            sheet.autoSizeColumn(1); // 调整第二列宽度
            sheet.autoSizeColumn(2); // 调整第三列宽度
            sheet.autoSizeColumn(3); // 调整第四列宽度
            sheet.autoSizeColumn(4); // 调整第四列宽度
        }
    }

    /**
     * 填充表头
     *
     * @param sheet sheet
     * @param style style
     * @param key   key
     * @param value value
     * @param index index
     */
    private static SXSSFRow setCellHead(SXSSFSheet sheet, CellStyle style, String key, String value,
                                        int index, SXSSFRow row, int cellIndex) {
        if (null == row) {
            row = sheet.createRow(index);
        }
        style.setAlignment(HorizontalAlignment.CENTER); // 创建一个居中格式
        SXSSFCell cell = row.createCell(cellIndex);
        cell.setCellStyle(style);
        cell.setCellValue(key);
        cell = row.createCell(cellIndex + 1);
        cell.setCellValue(value);
        return row;
    }

    /**
     * 导出账单-账单基本信息
     *
     * @param billNo billNo
     * @return TrdBillingListDto
     */
    private TrdBillingListDto findBillingInfo(String billNo) {
        TrdBillingListDto billingListDto = (TrdBillingListDto) dao.findForObject(("TrdBillingMapper.findBillingInfo"), billNo);
        if (null != billingListDto) {
            billingListDto.setAdjustment(adjustment(billingListDto.getAdjustmentTypeCode(), billingListDto.getAdjustmentValue(), billingListDto.getAdjustmentUnit()));
        }
        return billingListDto;
    }

    /**
     * 提交审核
     *
     * @param billNos 账单号集合
     */
    @Override
    public void submit(List<String> billNos) {
        if (CollectionUtils.isEmpty(billNos)) {
            return;
        }
        // 修改账单状态,重置是否打回过草稿状态
        updateBillingStatus(billNos, BillingStatus.DRAFT, BillingStatus.AUDITING, "", "账单审核", false);
    }

    /**
     * 审核账单
     *
     * @param map map
     */
    @Override
    public BillingStatus audit(Map<String, Object> map) {
        List<String> billNos = (List<String>) map.get("billNos");
        Object type = map.get("type");
        Object msg = map.get("msg");
        if (CollectionUtils.isEmpty(billNos) || null == type || null == msg) {
            throw Exceptions.bizException(ErrorCodes.ERROR_ARGUMENT_EMPTY, "");
        }
        BillingStatus statusTo;
        try {
            statusTo = BillingStatus.valueOf(String.valueOf(type));
        } catch (Exception e) {
            e.printStackTrace();
            throw Exceptions.bizException(ErrorCodes.ERROR_ARGUMENT_EMPTY, "");
        }
        BillingStatus statusFrom = null;
        Boolean isReject = false; // 打回草稿判断
        switch (statusTo) {
            case CHARGEOFF:
                statusFrom = BillingStatus.AUDITING;
                break;
            case DRAFT:
                statusFrom = BillingStatus.AUDITING;
                isReject = true;
                break;
            case PAID:
                statusFrom = BillingStatus.CHARGEOFF;
                break;
            default:
                break;
        }
        // 修改账单状态
        updateBillingStatus(billNos, statusFrom, statusTo, String.valueOf(msg), "账单审核及已付款", isReject);
        return statusTo;
    }

    /**
     * 价格调整
     *
     * @param billingDto
     */
    @Override
    public void adjustment(Map<String, String> billingDto) {
        String billNo = billingDto.get("billNo");
        String adjustmentTypeCode = billingDto.get("adjustmentTypeCode");
        String adjustmentValue = billingDto.get("adjustmentValue");
        String adjustmentUnit = billingDto.get("adjustmentUnit");
        String adjustmentRemark = billingDto.get("adjustmentRemark");
        if (StringUtils.isEmpty(adjustmentTypeCode) || StringUtils.isEmpty(adjustmentValue) || StringUtils.isEmpty(adjustmentUnit) || StringUtils.isEmpty(adjustmentRemark)) {
            throw Exceptions.bizException(ErrorCodes.ERROR_ARGUMENT_ILLEGAL, "");
        }
        // 校验调整价格数值
        checkAdjustmentValue(adjustmentValue, adjustmentUnit);
        // 只有待审核状态下可以调整
        TrdBilling billing = findByBillNo(billNo);
        if (null != billing && billing.getStatus().getCode().equals(BillingStatus.AUDITING.getCode())) {
            // 价格调整数据
            saveAdjustment(billNo, adjustmentTypeCode, adjustmentValue, adjustmentUnit, adjustmentRemark);
            // 审核日志
            String result = populateAdjustment(adjustmentTypeCode, adjustmentValue, adjustmentUnit);
            if (!StringUtils.isEmpty(result)) {
                result = "[" + result + "] ";
            }
            approvalService.approval(ApprovalService.ENTITY_TYPE_FINANCIAL,
                    billing.getBillNo(), BillingStatus.AUDITING, BillingStatus.ADJUSTPRICE,
                    result + adjustmentRemark, PrincipalUtils.getAccountId());
        } else {
            throw Exceptions.bizException(ErrorCodes.ERROR_FINANCIAL_AUDITING_CAN_ADJUST_PRICE);
        }
    }

    /**
     * 调整价格
     *
     * @param billNo             billNo
     * @param adjustmentTypeCode adjustmentTypeCode
     * @param adjustmentValue    adjustmentValue
     * @param adjustmentUnit     adjustmentUnit
     * @param adjustmentRemark   adjustmentRemark
     */
    private void saveAdjustment(String billNo, String adjustmentTypeCode, String adjustmentValue, String adjustmentUnit, String adjustmentRemark) {
        // 获取数据库当前时间
        Date now = dao.getDbDate();
        // 计算价格价格调整后实收费
        BigDecimal actualTotal = countFeeAfterAdjustment(billNo, adjustmentTypeCode, adjustmentValue, adjustmentUnit);
        // 价格调整数据
        BillingDto billingDtoNew = new BillingDto();
        billingDtoNew.setBillNo(billNo);
        billingDtoNew.setAdjustmentTypeCode(BillingAdjustmentTypeCode.valueOf(adjustmentTypeCode));
        billingDtoNew.setAdjustmentValue(new BigDecimal(adjustmentValue));
        billingDtoNew.setAdjustmentUnit(adjustmentUnit);
        billingDtoNew.setAdjustmentRemark(adjustmentRemark);
        billingDtoNew.setAdjustmentByUser(PrincipalUtils.getAccountId());
        billingDtoNew.setAdjustmentTime(now);
        billingDtoNew.setActualTotal(actualTotal);
        dao.update(("TrdBillingMapper.updateSelective"), billingDtoNew);
    }

    /**
     * 当调整的为“百分比”时，中间数值为5位（小数点精确到后三位） 当调整的为“金额”时，中间数值为10位（小数点精确到后三位）
     *
     * @param adjustmentValue adjustmentValue
     * @param adjustmentUnit  adjustmentUnit
     */
    private void checkAdjustmentValue(String adjustmentValue, String adjustmentUnit) {
        if (QuotationConstants.DiscountType.AMOUNT.equals(adjustmentUnit)) { // 金额
            if (!isNumber(adjustmentValue, AMOUNT_LEN)) {
                throw Exceptions.bizException(ErrorCodes.ERROR_FINANCIAL_ADJUSTMENT_VALUE_ERROR);
            }
        } else if (QuotationConstants.DiscountType.PERCENTAGE.equals(adjustmentUnit)) { // 百分比
            if (!isNumber(adjustmentValue, PERCENT_LEN)) {
                throw Exceptions.bizException(ErrorCodes.ERROR_FINANCIAL_ADJUSTMENT_VALUE_ERROR);
            }
        }
    }

    /**
     * 判断是否有效数字
     *
     * @param str str
     * @return boolean
     */
    private boolean isNumber(String str, int length) {
        Pattern pattern = Pattern.compile(NUMBER_REGEX);
        Matcher match = pattern.matcher(str);
        return match.matches() && str.length() <= length;
    }

    /**
     * 记录调整行为记录
     *
     * @param adjustmentTypeCode adjustmentTypeCode
     * @param adjustmentValue    adjustmentValue
     * @param adjustmentUnit     adjustmentUnit
     * @return String
     */
    private String populateAdjustment(String adjustmentTypeCode, String adjustmentValue, String adjustmentUnit) {
        String result = "";
        // 上调
        if (adjustmentTypeCode.equals(BillingAdjustmentTypeCode.DOWN.name())) {
            if (QuotationConstants.DiscountType.PERCENTAGE.equals(adjustmentUnit)) {
                result = "-" + adjustmentValue + "%";
            } else if (QuotationConstants.DiscountType.AMOUNT.equals(adjustmentUnit)) {
                result = "-" + adjustmentValue;
            }
        } else if (adjustmentTypeCode.equals(BillingAdjustmentTypeCode.UPPER.name())) { // 上调
            // 1： 百分比； 2： 金额
            if (QuotationConstants.DiscountType.PERCENTAGE.equals(adjustmentUnit)) {
                result = "+" + adjustmentValue + "%";
            } else if (QuotationConstants.DiscountType.AMOUNT.equals(adjustmentUnit)) {
                result = "+" + adjustmentValue;
            }
        }
        return result;
    }

    /**
     * 计算价格价格调整后实收费
     *
     * @param billNo             billNo
     * @param adjustmentTypeCode adjustmentTypeCode
     * @param adjustmentValue    adjustmentValue
     * @param adjustmentUnit     adjustmentUnit
     * @return BigDecimal
     */
    private BigDecimal countFeeAfterAdjustment(String billNo, String adjustmentTypeCode, String adjustmentValue, String adjustmentUnit) {
        // 获取账单详细信息
        TrdBilling billing = findByBillNo(billNo);
        if (null != billing) {
            // 判断价格调整类型
            BigDecimal total = billing.getTotal();
            BigDecimal actualTotal = null;
            // 上调
            if (adjustmentTypeCode.equals(BillingAdjustmentTypeCode.DOWN.name())) {
                if (QuotationConstants.DiscountType.PERCENTAGE.equals(adjustmentUnit)) {
                    actualTotal = total.multiply(BigDecimal.valueOf(1).subtract(new BigDecimal(adjustmentValue).divide(BigDecimal.valueOf(100), 3, BigDecimal.ROUND_UP)));
                } else if (QuotationConstants.DiscountType.AMOUNT.equals(adjustmentUnit)) {
                    actualTotal = total.subtract(new BigDecimal(adjustmentValue));
                }
            } else if (adjustmentTypeCode.equals(BillingAdjustmentTypeCode.UPPER.name())) { // 上调
                if (QuotationConstants.DiscountType.PERCENTAGE.equals(adjustmentUnit)) {
                    actualTotal = total.multiply(BigDecimal.valueOf(1).add(new BigDecimal(adjustmentValue).divide(BigDecimal.valueOf(100), 3, BigDecimal.ROUND_UP)));
                } else if (QuotationConstants.DiscountType.AMOUNT.equals(adjustmentUnit)) {
                    actualTotal = total.add(new BigDecimal(adjustmentValue));
                }
            }
            return actualTotal;
        }
        return null;
    }

    /**
     * 修改账单状态
     *
     * @param billNos            账单号
     * @param approvalStatusFrom 账单当前状态
     * @param approvalStatusTo   要修改成的状态
     * @param msg                流转信息
     * @param logMsg             日志信息
     */
    private void updateBillingStatus(List<String> billNos, BillingStatus approvalStatusFrom,
                                     BillingStatus approvalStatusTo, String msg,
                                     String logMsg, Boolean isReject) {
        Map<String, Object> paramMap = new HashMap<>();
        paramMap.put("billNos", billNos);
        List<TrdBilling> bills = dao.findForList(("TrdBillingMapper.findActivedByBillNos"), paramMap, TrdBilling.class);
        List<String> billNoSaved = new ArrayList<>();
        for (TrdBilling billing : bills) {
            if (approvalStatusFrom.getCode().equals(billing.getStatus().getCode())) {
                billNoSaved.add(billing.getBillNo());
                // 审核日志
                approvalService.approval(ApprovalService.ENTITY_TYPE_FINANCIAL,
                        billing.getBillNo(), approvalStatusFrom, approvalStatusTo, msg, PrincipalUtils.getAccountId());
            } else {
                // 禁止状态跳跃
                throw Exceptions.bizException(ErrorCodes.ERROR_FINANCIAL_STATUS_CAN_NOT_JUMP,
                        approvalStatusFrom.getName(), approvalStatusTo.getName());
            }
        }
        if (CollectionUtils.isEmpty(billNoSaved)) {
            return;
        }
        paramMap = new HashMap<>();
        paramMap.put("status", approvalStatusTo);
        paramMap.put("billNos", billNoSaved);
        paramMap.put("lastUpdateUserId", PrincipalUtils.getAccountId());
        paramMap.put("isRejected", isReject);
        if (approvalStatusTo.getCode().equals(BillingStatus.CHARGEOFF.getCode())) {
            paramMap.put("billTime", dao.getDbDate());
        }
        dao.update(("TrdBillingMapper.updateStatus"), paramMap);
        if (approvalStatusTo.getCode().equals(BillingStatus.CHARGEOFF.getCode())) {
            // 生成账单excel更新账单文件路径
            exportBills(billNos);
        }
        // 保存操作日志
        Map<String, Object> dataBackup = new HashMap<>();
        dataBackup.put("UPDATE_BILLING", paramMap);
        PrincipalLogUtils.addOperationLog(ModuleNames.BIZ, ModuleNames.BIZ_FINANCIAL, UserActions.UPDATE, logMsg, dataBackup);
    }

    /**
     * 重新生成账单
     *
     * @param trdBillingTaskQueryDto trdBillingTaskQueryDto
     */
    @Override
    public void regenerate(TrdBillingTaskQueryDto trdBillingTaskQueryDto) {
        generateBill(trdBillingTaskQueryDto);
    }

    /**
     * 根据任务编码获取任务
     *
     * @param taskCodes taskCodes
     * @return
     */
    public List<TrdBilling> findByTaskCodes(List<String> taskCodes) {
        ParamData paramData = new ParamData();
        paramData.put("taskCodes", taskCodes);
        return dao.findForList(("TrdBillingMapper.findByTaskCodes"), paramData, TrdBilling.class);
    }

    /**
     * 生成账单
     *
     * @param trdBillingTaskQueryDto trdBillingTaskQueryDto
     */
    @Override
    public void generateBill(TrdBillingTaskQueryDto trdBillingTaskQueryDto) {
        //生成主帐号账单
        newAndRetryGenerate(trdBillingTaskQueryDto);
    }

    /**
     * 生成或者重新生成账单
     *
     * @param trdBillingTaskQueryDto trdBillingTaskQueryDto
     */
    private void newAndRetryGenerate(TrdBillingTaskQueryDto trdBillingTaskQueryDto) {
        // 验证账单周期时间
        validateStartEndTime(trdBillingTaskQueryDto);
        // 获取(用户)时间段内未生成账单的所有订单
        List<TrdBillingGenerateDto> ordServiceBillList = getOrderByBizCompanyIdAndCreateTime(trdBillingTaskQueryDto, false);
        // 获取(用户)时间段内已经生成账单的所有订单
        List<TrdBillingGenerateDto> ordServiceBillListGenerated = getOrderByBizCompanyIdAndCreateTime(trdBillingTaskQueryDto, true);
        // 更新已经跳过订单数
        if (null != ordServiceBillListGenerated && ordServiceBillListGenerated.size() > 0) {
            TrdBillingTask ordersGenerated = new TrdBillingTask();
            ordersGenerated.setSkippedOrderQty(ordServiceBillListGenerated.size());
            ordersGenerated.setTaskCode(trdBillingTaskQueryDto.getTaskCode());
            trdBillingTaskService.updateBillingTask(ordersGenerated);
        }
        // 添加新的账单
        if (!CollectionUtils.isEmpty(ordServiceBillList)) {
            Map<String, List<TrdBillingGenerateDto>> map = populateGenerateData(ordServiceBillList);
            if (!map.isEmpty()) {
                for (Map.Entry<String, List<TrdBillingGenerateDto>> entry : map.entrySet()) {
                    String key = entry.getKey();
                    Integer bizCompanyId = Integer.parseInt(key.split("-")[0]);
                    Integer platformId = Integer.parseInt(key.split("-")[1]);
                    TrdBilling billing = populateBilling(bizCompanyId, platformId, trdBillingTaskQueryDto.getType(),
                            trdBillingTaskQueryDto.getStartEffectTime(),
                            trdBillingTaskQueryDto.getEndEffectTime(), trdBillingTaskQueryDto.getTaskCode(), entry.getValue());
                    saveBilling(billing, entry.getValue());
                }
            }
        }
    }

    /**
     * 没有客户的订单数据准备
     *
     * @param ordServiceBillList ordServiceBillList
     * @return Map
     */
    private Map<String, List<TrdBillingGenerateDto>> populateGenerateData(List<TrdBillingGenerateDto> ordServiceBillList) {
        Map<String, List<TrdBillingGenerateDto>> map = new HashMap<>();
        for (TrdBillingGenerateDto TrdBillingGenerateDto : ordServiceBillList) {
            String key = TrdBillingGenerateDto.getBizCompanyId() + "-" + TrdBillingGenerateDto.getPlatformId();
            List<TrdBillingGenerateDto> customerOrderList = map.get(key);
            if (null == customerOrderList) {
                customerOrderList = new ArrayList<>();
            }
            customerOrderList.add(TrdBillingGenerateDto);
            map.put(key, customerOrderList);
        }
        return map;
    }

    /**
     * 保存账单
     *
     * @param billing   billing
     * @param orderList orderList
     */
    private void saveBilling(TrdBilling billing, List<TrdBillingGenerateDto> orderList) {
        try {
            dao.save(("TrdBillingMapper.insertSelective"), billing);
            saveBillingOrderRel(billing, orderList);
            // 更新账单任务已经生成账单客户数量
            trdBillingTaskService.updateTaskCustomerComplete(billing.getTaskCode(), orderList.size());
            // 审核日志
            approvalService.approval(ApprovalService.ENTITY_TYPE_FINANCIAL,
                    billing.getBillNo(), BillingStatus.GENERATION, BillingStatus.DRAFT, "生成账单",
                    PrincipalUtils.getAccountId());
        } catch (Exception e) {
            List<String> taskCodes = new ArrayList<>();
            taskCodes.add(billing.getTaskCode());
            trdBillingTaskService.updateTaskStatus(BillingTaskStatus.SUSPEND, taskCodes);
            updateIsBilled(billing.getType(), orderList);
            e.printStackTrace();
        }
    }

    /**
     * 更新订单是否已经生成订单状态
     *
     * @param type      1销售账单 2采购账单
     * @param orderList orderList
     */
    private void updateIsBilled(int type, List<TrdBillingGenerateDto> orderList) {
        List<String> orderNos = orderList.stream().map(TrdBillingGenerateDto::getOrderNo).collect(Collectors.toList());
        // 更新订单为已经生成订单状态
        trdOrderService.updateIsBilledStatus(orderNos, type, false);
    }

    /**
     * 保存账单订单关联
     *
     * @param billing           billing
     * @param customerOrderList customerOrderList
     */
    private void saveBillingOrderRel(TrdBilling billing, List<TrdBillingGenerateDto> customerOrderList) {
        // 根据账单号删除账单下的所有订单
        trdBillingOrderRelService.deleteByBillNo(billing.getBillNo(), billing.getType());
        // 添加新信息
        for (TrdBillingGenerateDto TrdBillingGenerateDto : customerOrderList) {
            TrdBillingOrderRel billingOrderRel = new TrdBillingOrderRel();
            billingOrderRel.setTaskCode(billing.getTaskCode());
            billingOrderRel.setBillNo(billing.getBillNo());
            billingOrderRel.setOrderNo(TrdBillingGenerateDto.getOrderNo());
            trdBillingOrderRelService.save(billingOrderRel);
            // 更新订单为已经生成订单状态
            List<String> orderNos = new ArrayList<>();
            orderNos.add(TrdBillingGenerateDto.getOrderNo());
            trdOrderService.updateIsBilledStatus(orderNos, billing.getType(), true);
        }
    }

    /**
     * 账单列表
     *
     * @param params params
     * @return QResultDto
     */
    @Override
    public QResultDto findByFilter(QueryParams params) {
        ParamData pd = convertQueryParams(params);
        //根据条件获取账单列表
        List<TrdBillingListDto> trdBillingList = dao.findForList(("TrdBillingMapper.findByFilterListPage"), pd, TrdBillingListDto.class);
        if (null != trdBillingList && trdBillingList.size() > 0) {
            trdBillingList.forEach(trdBilling -> {
                trdBilling.setAdjustment(adjustment(trdBilling.getAdjustmentTypeCode(), trdBilling.getAdjustmentValue(), trdBilling.getAdjustmentUnit()));
                //如果没有价格调整： 实收=应收
                if (StringUtils.isEmpty(trdBilling.getAdjustmentTypeCode())) {
                    trdBilling.setActualTotal(trdBilling.getTotal().setScale(2, BigDecimal.ROUND_HALF_UP));
                } else {
                    trdBilling.setActualTotal(trdBilling.getActualTotal().setScale(2, BigDecimal.ROUND_HALF_UP));
                }
                trdBilling.setStatusName(trdBilling.getStatus().getName());
                trdBilling.setTotal(trdBilling.getTotal().setScale(3, BigDecimal.ROUND_HALF_UP));
                if (trdBilling.getType() == 2) {
                    Supplier sup = supplierService.findSimpleById(trdBilling.getBizCompanyId());
                    trdBilling.setBizCompanyName(sup.getName());
                    trdBilling.setBizCompanyCode(sup.getCode());
                }
            });
        }
        return new QResultDto(trdBillingList, pd.getPagination());
    }

    /**
     * 账单列表-客户平台
     *
     * @param billNo          billNo
     * @param orderNo         orderNo
     * @param cusUserId       cusUserId
     * @param status          status
     * @param includeAllAudit includeAllAudit
     * @param startEffectTime startEffectTime
     * @param endEffectTime   endEffectTime
     * @param pageIndex       pageIndex
     * @param pageSize        pageSize
     * @param sort            sort
     * @param asc             asc
     * @return QResultDto
     */
    @Override
    public QResultDto findByFilterForCustomer(Integer platformId, String billNo, String orderNo, Integer cusUserId,
                                              BillingStatus status, boolean includeAllAudit, String startEffectTime,
                                              String endEffectTime, int pageIndex, int pageSize, String sort, Boolean asc) {
        //查询参数
        QueryParams params = new QueryParams(pageSize, pageIndex, "", sort, asc);
        params.put("startEffectTime", StringUtils.isEmpty(startEffectTime) ? null : startEffectTime);
        params.put("endEffectTime", StringUtils.isEmpty(endEffectTime) ? null : endEffectTime);
        params.put("billNo", billNo);
        params.put("orderNo", orderNo);
        params.put("platformId", platformId);
        params.put("status", status);
        params.put("includeAllAudit", includeAllAudit);
        params.put("taskStatus", BillingTaskStatus.COMPLETED);

        List<BillingListCustomerDto> billingList = new ArrayList<>();
        ParamData pd = new ParamData();
        //帐号信息
        CusUser primaryAccount = isPrimaryAccount(cusUserId);
        SupplierDto supplierDto = supplierService.findCurrTrdSupplier();
        if (null != primaryAccount) {
            if (!primaryAccount.getIsSystem() || null != supplierDto) {
                List<Integer> customerIds = new ArrayList<>();
                Integer isSup = 1;
                Integer supId = null;
                if (null != supplierDto) {
                    params.put("isSup", 1);
                    supId = supplierDto.getId();
                    customerIds.add(supplierDto.getId());
                } else {
                    isSup = 0;
                    params.put("isSup", 0);
                    customerIds.add(primaryAccount.getId());
                }

                params.put("userIds", customerIds);
                pd = convertQueryParams(params);
                billingList = dao.findForList(("TrdBillingMapper.findByFilterListPageForCustomer"), pd, BillingListCustomerDto.class);
                //公共
                if (null != billingList && billingList.size() > 0) {
                    billingList.forEach(billing -> {
                        billing.setStatusName(billing.getStatus().getName());
                        PlatformCompany pc = platformCompanyService.getPlatformCompanyById(billing.getPlatformId());
                        billing.setPlatformName(pc.getName());
                    });
                }


                //计算子帐号下所有订单金额
                if (null != billingList && billingList.size() > 0) {
                    Integer finalIsSup = isSup;
                    Integer finalSupId = supId;
                    billingList.forEach(billing -> {
                        Map<String, Object> amountSub = null;
                        if (finalIsSup == 0) {
                            amountSub = trdBillingOrderRelService.getOrderAmountSub(billing.getBillNo(), finalIsSup, cusUserId);
                        } else {
                            amountSub = trdBillingOrderRelService.getOrderAmountSub(billing.getBillNo(), finalIsSup, finalSupId);
                        }
                        //子帐号
                        if (null != amountSub && finalSupId == null && !primaryAccount.getIsSystem()) {
                            billing.setIsShowAdjustment(false);
                            billing.setAdjustment(null);
                            billing.setAdjustmentUnit(null);
                            billing.setAdjustmentTypeCode(null);
                            billing.setAdjustmentValue(null);
                            billing.setTotal(((BigDecimal) amountSub.get("total")).setScale(3, BigDecimal.ROUND_HALF_UP));
                            billing.setActualTotal(billing.getTotal().setScale(2, BigDecimal.ROUND_HALF_UP));
                            billing.setAdjustmentRemark(null);
                        }

                        if (finalSupId != null) {
                            billing.setIsShowAdjustment(billing.getAdjustmentUnit() != null);
                            billing.setAdjustment(adjustment(billing.getAdjustmentTypeCode(), billing.getAdjustmentValue(), billing.getAdjustmentUnit()));
                            if (!billing.getIsShowAdjustment()) {
                                billing.setActualTotal(billing.getTotal());
                            }
                        }
                    });
                }

            } else {  //主帐号
                List<Integer> customerIds = new ArrayList<>();
                //获取账户下所有帐号
                List<CusUser> cusUsers = customerUserService.findByCustomerIdIncludeDeleted(primaryAccount.getRefCustomerId());
                if (null != cusUsers && cusUsers.size() > 0) {
                    cusUsers.forEach(cusUser -> {
                        customerIds.add(cusUser.getId());
                    });
                }
                params.put("isSup", 0);
                params.put("userIds", customerIds);
                pd = convertQueryParams(params);
                billingList = dao.findForList(("TrdBillingMapper.findByFilterListPageForCustomer"), pd, BillingListCustomerDto.class);
                if (null != billingList && billingList.size() > 0) {
                    billingList.forEach(billing -> {
                        billing.setAdjustment(adjustment(billing.getAdjustmentTypeCode(), billing.getAdjustmentValue(), billing.getAdjustmentUnit()));
                        billing.setIsShowAdjustment(true);
                        //如果没有价格调整： 实收=应收
                        if (StringUtils.isEmpty(billing.getAdjustmentTypeCode())) {
                            billing.setActualTotal(billing.getTotal().setScale(2, BigDecimal.ROUND_HALF_UP));
                        } else {
                            billing.setActualTotal(billing.getActualTotal().setScale(2, BigDecimal.ROUND_HALF_UP));
                        }
                        billing.setStatusName(billing.getStatus().getName());
                        billing.setTotal(billing.getTotal().setScale(3, BigDecimal.ROUND_HALF_UP));
                        PlatformCompany pc = platformCompanyService.getPlatformCompanyById(billing.getPlatformId());
                        billing.setPlatformName(pc.getName());
                    });
                }
            }
        }
        return new QResultDto(billingList, pd.getPagination());
    }

    /**
     * 是否主帐号
     *
     * @param customerId customerId
     * @return CusUser
     */
    private CusUser isPrimaryAccount(Integer customerId) {
        return customerUserService.findById(customerId);
    }

    /**
     * 优惠价格
     *
     * @param adjustmentTypeCode adjustmentTypeCode
     * @param adjustmentValue    adjustmentValue
     * @param adjustmentUnit     adjustmentUnit
     * @return String
     */
    private String adjustment(BillingAdjustmentTypeCode adjustmentTypeCode, BigDecimal adjustmentValue, String adjustmentUnit) {

        adjustmentValue = adjustmentValue == null ? BigDecimal.ZERO : adjustmentValue;
        String adjustmentUnitPS = "";
        String adjustmentType = "";

        // 上调
        if (null != adjustmentTypeCode && adjustmentTypeCode.getCode().equals(BillingAdjustmentTypeCode.UPPER.getCode())) {
            adjustmentType = "+";
        } else if (null != adjustmentTypeCode && adjustmentTypeCode.getCode().equals(BillingAdjustmentTypeCode.DOWN.getCode())) { // 下调
            adjustmentType = "-";
        }
        // adjustmentUnit: 1 : %; 2: 金额
        if (null != adjustmentUnit && QuotationConstants.DiscountType.PERCENTAGE.equals(adjustmentUnit)) {
            adjustmentUnitPS = "%";
        }
        return adjustmentType + adjustmentValue + adjustmentUnitPS;
    }


    /**
     * 根据账单号获取账单信息
     *
     * @param billNo billNo
     * @return TrdBilling
     */
    @Override
    public TrdBilling findByBillNo(String billNo) {
        return (TrdBilling) dao.findForObject(("TrdBillingMapper.findByBillNo"), billNo);
    }

    /**
     * 所有价格调整类型
     *
     * @return List
     */
    @Override
    public List<CommonModel> getAdjustmentType() {
        List<CommonModel> list = Lists.newArrayList();
        for (BillingAdjustmentTypeCode billingAdjustmentTypeCode : BillingAdjustmentTypeCode.values()) {
            CommonModel dto = new CommonModel(billingAdjustmentTypeCode.name(), billingAdjustmentTypeCode.getName());
            list.add(dto);
        }
        return list;
    }

    /**
     * 所有账单状态-客户平台
     *
     * @return List
     */
    @Override
    public List<CommonModel> getBillingStatusForCustomer() {
        List<CommonModel> list = Lists.newArrayList();
        for (BillingStatus billingStatus : BillingStatus.values()) {
            if (billingStatus.name().equals(BillingStatus.CHARGEOFF.name()) || billingStatus.name().equals(BillingStatus.PAID.name()) || billingStatus.name().equals(BillingStatus.OVERDUE.name())) {
                CommonModel dto = new CommonModel(billingStatus.name(), billingStatus.getName());
                list.add(dto);
            }
        }
        return list;
    }

    /**
     * 所有账单状态
     *
     * @param isAudit isAudit
     * @return List
     */
    @Override
    public List<CommonModel> getBillingStatus(String isAudit) {
        List<CommonModel> list = Lists.newArrayList();
        for (BillingStatus billingStatus : BillingStatus.values()) {
            if (!StringUtils.isEmpty(isAudit) && "1".equals(isAudit)) {
                if (billingStatus.name().equals(BillingStatus.AUDITING.name())
                        || billingStatus.name().equals(BillingStatus.PAID.name())
                        || billingStatus.name().equals(BillingStatus.CHARGEOFF.name())
                        || billingStatus.name().equals(BillingStatus.OVERDUE.name())) {
                    CommonModel dto = new CommonModel(billingStatus.name(), billingStatus.getName());
                    list.add(dto);
                }
            } else {
                if (billingStatus.name().equals(BillingStatus.DRAFT.name())
                        || billingStatus.name().equals(BillingStatus.CHARGEOFF.name())
                        || billingStatus.name().equals(BillingStatus.AUDITING.name())
                        || billingStatus.name().equals(BillingStatus.PAID.name())
                        || billingStatus.name().equals(BillingStatus.OVERDUE.name())) {
                    CommonModel dto = new CommonModel(billingStatus.name(), billingStatus.getName());
                    list.add(dto);
                }
            }
        }
        return list;
    }

    /**
     * 组装账单信息
     *
     * @param bizCompanyId       bizCompanyId
     * @param platformId         platformId
     * @param startEffectTime    startEffectTime
     * @param endEffectTime      endEffectTime
     * @param taskCode           taskCode
     * @param ordServiceBillList ordServiceBillList
     * @return TrdBilling
     */
    private TrdBilling populateBilling(Integer bizCompanyId, Integer platformId, int type,
                                       Date startEffectTime, Date endEffectTime,
                                       String taskCode,
                                       List<TrdBillingGenerateDto> ordServiceBillList) {
        // 获取货币类型
        String currencyType = null;
        if (null != ordServiceBillList && ordServiceBillList.size() > 0) {
            currencyType = ordServiceBillList.get(0).getCurrencyType();
        }
        // 组装账单信息
        String uid = generateNewUid();
        TrdBilling billing = new TrdBilling();
        billing.setBillNo(uid);
        billing.setBizCompanyId(bizCompanyId);
        billing.setPlatformId(platformId);
        billing.setStatus(BillingStatus.DRAFT);
        billing.setCurrencyType(currencyType);
        // 计算订单金额综总和
        BigDecimal total = null;
        if (null != ordServiceBillList) {
            total = countBillingMoney(ordServiceBillList);
        }
        billing.setTotal(total);
        billing.setIsActive(true);
        if (StringUtils.isEmpty(taskCode)) {
            billing.setTaskCode(generateNewUid());
        } else {
            billing.setTaskCode(taskCode);
        }
        billing.setReleaseTime(new Date());
        billing.setStartEffectTime(startEffectTime);
        billing.setEndEffectTime(endEffectTime);
        billing.setType(type);
        billing.setIsDeleted(false);
        billing.setCreateUserId(PrincipalUtils.getAccountId());
        billing.setLastUpdateUserId(PrincipalUtils.getAccountId());
        return billing;
    }

    /**
     * 账单实收款
     *
     * @param ordServiceBillList ordServiceBillList
     * @return BigDecimal
     */
    private BigDecimal countBillingMoney(List<TrdBillingGenerateDto> ordServiceBillList) {
        BigDecimal total = new BigDecimal(0);
        for (TrdBillingGenerateDto dto : ordServiceBillList) {
            if (null != dto.getActualTotal()) {
                total = total.add(dto.getActualTotal());
            }
        }
        return total;
    }

    /**
     * 获取时间段内所有订单信息
     *
     * @param trdBillingTaskQueryDto trdBillingTaskQueryDto
     * @param isBilled               isBilled
     * @return List
     */
    @Override
    public List<TrdBillingGenerateDto> getOrderByBizCompanyIdAndCreateTime(TrdBillingTaskQueryDto trdBillingTaskQueryDto, boolean isBilled) {
        List<TrdBillingGenerateDto> ordServiceBillList = null;
        try {
            Integer type = trdBillingTaskQueryDto.getType();
            List<Integer> bizCompanyIds = trdBillingTaskQueryDto.getBizCompanyIds();
            List<Integer> platformIds = trdBillingTaskQueryDto.getPlatformIds();
            ordServiceBillList = trdOrderService.getOrderByCreatBillQuery(type, bizCompanyIds, platformIds,
                    trdBillingTaskQueryDto.getStartEffectTime(), trdBillingTaskQueryDto.getEndEffectTime(), isBilled);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return ordServiceBillList;
    }

    /**
     * 验证时间段是否正确
     *
     * @param billingTaskDto billingTaskDto
     */
    private void validateStartEndTime(TrdBillingTaskQueryDto billingTaskDto) {
        // 判断查询订单的时间段参数是否存正确
        if (null == billingTaskDto || StringUtils.isEmpty(billingTaskDto.getStartEffectTime())
                || StringUtils.isEmpty(billingTaskDto.getEndEffectTime())) {
            throw Exceptions.bizException(ErrorCodes.ERROR_ARGUMENT_ILLEGAL);
        }
    }

    @Override
    protected String getMapperNamespace() {
        return "TrdBillingMapper";
    }
}
